﻿(*                  FnSharp              

   Base class library extensions and frameworks aimed 
   specifically at improving F# developers lives!

   Written by: Huw Simpson
   License: Microsoft Public License (Ms-PL) 
   Available at: http://fnsharp.codeplex.com

*)
namespace FnSharp.Bcl.IO

    open System
    open System.IO

    [<RequireQualifiedAccess>]
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module Path =
        let parse (pattern:String) =
            pattern
            |> String.toLower
            |> String.groupBy2 (fun c g ->
                match c with
                | '\\' | '/' -> failwith "Invalid search pattern!"
                | '*' when g.EndsWith "*" -> ContinueGroup (WithoutItem)
                | '*' -> YieldGroup (GroupItem)
                | '?' when g.EndsWith "*" -> ContinueGroup (WithoutItem)
                | '?' -> YieldGroup (GroupItem)
                | c when g.EndsWith "*" -> YieldGroup (GroupItem)
                | c when g.EndsWith "?" -> YieldGroup (GroupItem)
                | c -> ContinueGroup (WithItem))

        let matchPattern (pattern:String, fileName:String) =
            let ptr = ref 0
            let lastMatch = ref ""
            let getPortion () = fileName |> String.skip !ptr |> String.toLower

            pattern
            |> parse
            |> Array.map (fun pattern -> 
                match pattern, getPortion () with
                | "*",s ->
                    lastMatch := "*"
                    true
                | "?",s when s.Length = 0 ->
                    ptr := !ptr + 1 
                    lastMatch := ""
                    false
                | "?",s -> 
                    ptr := !ptr + 1
                    lastMatch := "?"
                    true
                | str,s when !lastMatch = "*" && s.Contains str ->
                    ptr := !ptr + str.Length
                    lastMatch := str
                    true
                | str,s when s.StartsWith str ->
                    ptr := !ptr + str.Length
                    lastMatch := str
                    true
                | _ -> false )
            |> Array.exists ((=) false) |> not